# -*- coding:utf-8 -*-

import os
import sys
import re
import json
import subprocess

from PyQt5.QtCore import QUrl, QThread, pyqtSignal
from PyQt5.QtGui import QIcon, QPixmap, QMovie, QDesktopServices
from PyQt5.QtWidgets import QApplication, QDialog, QFileDialog, QMessageBox, QTreeWidgetItem

from pySetupDialogDesigner import Ui_pySetupDialog

# qrc生成都图片二进制资源文件，不是普通的Python模块，以数据文件形式
# 必须引用
import resource


def make_run_bat(bat_filepath):
    '''
    '''
    bat_content = '''@ECHO OFF\n\n''' + \
                '''SET batFileDir=%cd%\n''' + \
                '''SET pythonExeFilePath=/python/python.exe\n''' + \
                '''SET MainAppFilePath=/src/main.py\n''' + \
                '''SET pythonExeAbsFilePath=%batFileDir%%pythonExeFilePath%\n''' + \
                '''REM SET pythonExeAbsFilePath=Python\n''' + \
                '''SET MainAppAbsFilePath=%batFileDir%%MainAppFilePath%\n''' + \
                '''SET "spaceChar= "\n''' + \
                '''SET RunCommand=%pythonExeAbsFilePath%%spaceChar%%MainAppAbsFilePath%\n\n''' + \
                '''ECHO.\n\n''' + \
                '''ECHO iPyGIRS(Version 0.2.4) application is starting, please wait a moment...\n\n''' + \
                '''ECHO.\n\n''' + \
                '''%RunCommand%\n\n''' + \
                '''PAUSE'''
    #
    with open(bat_filepath, "w+") as fid:
        fid.write(bat_content)

def load_packages_info(filename, installation_style = "online"):
    packages_info = []
    with open(filename, 'r', encoding="utf-8") as f:
        json_file_content = json.load(f)
        if installation_style == "online":
            packages_obj = json_file_content.get("online_packages")
        elif installation_style == "offline":
            packages_obj = json_file_content.get("offline_packages")
        else:
            return packages_info, "Invalid params 'installation_style' found!"
        #
        for json_obj in packages_obj:
            package_name = json_obj.get("name", "")
            package_version = json_obj.get("version", "")
            package_status = json_obj.get("status", "")
            if package_name == "" or package_version == "":
                return packages_info, "Invalid packages config file found!"
            if package_status == "":
                package_status = False
            #
            packages_info.append([package_name, package_version, package_status])
    #
    return packages_info, None

def check_exe_path(exe_path):
    '''
    单独处理Windows上，可执行程序的路径中存在类似“Program Files”这样的空格，而导致程序无法执行的情况
    方法是：以路径分隔符分割字符串，判断是否子字符串中是否存在空格，存在，则将其替换为双引号引起来的新子字符串
    '''
    # 统一将路径分隔符转换为Linux系统的正斜杠"/"
    exe_path_ = re.sub(r'\\', '/', exe_path)
    sub_str_list = exe_path_.split("/")
    
    # 获取驱动器的名称，加上路径分隔符
    driver_name = sub_str_list[0] + os.path.sep
    sub_str_list_ = []
    for sub_str in sub_str_list[1:]:
        if len(re.findall(r"^.+(\s).+$", sub_str)) != 0:
            sub_str = "\"" + sub_str + "\""
        sub_str_list_.append(sub_str)
    return os.path.join(driver_name, *sub_str_list_)

class iPyGIRSInstallerDialog(QDialog, Ui_pySetupDialog):

    # packages_info = (
    #         ("numpy-1.18.5+mkl-cp37-cp37m-win_amd64.whl", "1.18.5" , False),
    #         ("numba-0.46.0-cp37-cp37m-win_amd64.whl", "0.46.0" , False),
    #         ("numexpr-2.7.0-cp37-cp37m-win_amd64.whl", "2.7.0" , False),
    #         ("scipy-1.3.1-cp37-cp37m-win_amd64.whl", "1.3.1" , False),
    #         ("python_dateutil-2.8.0-py2.py3-none-any.whl", "2.8.0" , False),
    #         ("xlrd-1.2.0-py2.py3-none-any.whl", "1.2.0" , False),
    #         ("et_xmlfile-1.0.1-py2.py3-none-any.whl", "1.0.1", False),
    #         ("openpyxl-3.0.0-py3-none-any.whl", "3.0.0" , False),
    #         ("pandas-0.25.2-cp37-cp37m-win_amd64.whl", "0.25.2" , False),
    #         ("Pillow-6.1.0-cp37-cp37m-win_amd64.whl", "6.1.0" , False),
    #         ("networkx-2.4-py3-none-any.whl", "2.4" , False),
    #         ("PyWavelets-1.1.1-cp37-cp37m-win_amd64.whl", "1.1.1" , False),
    #         ("pyparsing-2.4.2-py2.py3-none-any.whl", "2.4.2" , False),
    #         ("matplotlib-3.1.1-cp37-cp37m-win_amd64.whl", "3.1.1" , False),
    #         ("scikit_image-0.16.2-cp37-cp37m-win_amd64.whl", "0.16.2" , False),
    #         ("GDAL-2.4.1-cp37-cp37m-win_amd64.whl", "2.4.1" , False),
    #         ("rasterio-1.0.24+gdal24-cp37-cp37m-win_amd64.whl", "1.0.24" , False),
    #         ("Fiona-1.8.6-cp37-cp37m-win_amd64.whl", "1.8.6" , False),
    #         ("dbfread-2.0.7-py2.py3-none-any.whl", "2.0.7" , False),
    #         ("PyMySQL-0.9.3-py2.py3-none-any.whl", "0.9.3" , False),
    #         ("joblib-0.13.2-py2.py3-none-any.whl", "0.13.2" , False),
    #         ("psutil-5.6.3-cp37-cp37m-win_amd64.whl", "5.6.3" , False),
    #         ("scikit_learn-0.21.3-cp37-cp37m-win_amd64.whl", "0.21.3" , False),
    #         ("PyQt5-5.13.0-5.13.0-cp35.cp36.cp37.cp38-none-win_amd64.whl", "5.13.0" , False)
    #     )

    # packages_info = (
    #         ("numpy+mkl", "1.18.5" , False),
    #         ("numba", "0.53.1" , False),
    #         ("numexpr", "2.7.3" , False),
    #         ("scipy", "1.6.3" , False),
    #         # ("python_dateutil-2.8.0-py2.py3-none-any.whl", "2.8.0" , False),
    #         ("xlrd", "1.2.0" , False),
    #         # ("et_xmlfile-1.0.1-py2.py3-none-any.whl", "1.0.1", False),
    #         ("openpyxl", "3.0.7" , False),
    #         ("pandas", "1.2.5" , False),
    #         ("Pillow", "8.3.1" , False),
    #         # ("pyparsing-2.4.2-py2.py3-none-any.whl", "2.4.2" , False),
    #         ("matplotlib", "3.4.2" , False),
    #         ("scikit-image", "0.18.2" , False),
    #         ("GDAL", "3.2.3" , False),
    #         ("rasterio", "1.2.6" , False),
    #         ("Fiona", "1.8.20" , False),
    #         ("dbfread", "2.0.7" , False),
    #         ("PyMySQL", "0.9.3" , False),
    #         ("joblib", "0.13.2" , False),
    #         ("scikit-learn", "0.24.2" , False),
    #         ("PyQt5", "5.15.4" , False)
    #     )

    stop_thread_signal = pyqtSignal()

    def __init__(self):
        super(iPyGIRSInstallerDialog, self).__init__(None)
        # 初始化部分UI元素1
        self.setupUi(self)
        #
        # self.qSetting = setting
        self.package_filename = "../conf/packages.json"
        if not os.path.isfile(self.package_filename):
            QMessageBox.critical(self, "错误", '安装程序丢失了依赖包配置文件"packages.json", 请检查安装程序是否完整！')
            sys.exit()

        self.get_pip_filename = "../scripts/get-pip.py"
        if not os.path.isfile(self.get_pip_filename):
            QMessageBox.critical(self, "错误", '安装程序丢失了包管理器获取文件"get-pip.py", 请检查安装程序是否完整！')
            sys.exit()
        #
        # 初始化部分UI元素2
        self.init_ui_element()
        #
        # 连接控件的信号与槽
        self.connect_signal_slot()

    # def init_app_config(self):
    #     #
    #     self.setting_filename = os.path.dirname("./conf/setting.ini")
    #     self.qSetting = QSettings(self.setting_filename, QSettings.IniFormat)
    #     #
    #     self.python_interpreter = str(self.qSetting.value("pythonInterpreter"))
    #     self.pip_installed = int(self.qSetting.value("pipInstalled"))
    #     self.pipwin_installed = int(self.qSetting.value("pipwinInstalled"))

    def update_treewidget(self, installation_style="online"):
        for packageInfo in self.packages_info:
            treeWidgetItem = QTreeWidgetItem(self.packagesInfoTreeWidget)
            treeWidgetItem.setIcon(0, self.pypi_logo)
            treeWidgetItem.setText(0, packageInfo[0])
            treeWidgetItem.setText(1, packageInfo[1])
            if installation_style =="offline":
                if packageInfo[2] == False:
                    treeWidgetItem.setIcon(2, self.missing_state_logo)
                    treeWidgetItem.setText(2, "未找到")
                else:
                    treeWidgetItem.setIcon(2, self.ready_state_logo)
                    treeWidgetItem.setText(2, "已就绪")
            else:
                # "online":
                treeWidgetItem.setIcon(2, self.ready_state_logo)
                treeWidgetItem.setText(2, "已就绪")

    def init_ui_element(self):
        self.setWindowTitle("pySetup安装程序-V0.2.4")
        self.setWindowIcon(QIcon(":/icons/installer.ico"))
        self.setup_app_Logo = QPixmap(":/icons/setup.png")

        self.show_code_logo = QPixmap(":/icons/show_code_16x16.png")
        self.hide_code_logo = QPixmap(":/icons/hide_code_16x16.png")
        self.selectDirIcon = QIcon(":/icons/select_folder.ico")

        self.processing_tip_logo = QPixmap(":/icons/processing_tip_16x16.png")
        self.finish_tip_logo = QPixmap(":/icons/finish_tip_16x16.png")
        self.fail_tip_logo = QPixmap(":/icons/fail_state_16x16.png")

        self.pypi_logo = QIcon(":/icons/Python.png")
        self.missing_state_logo = QIcon(":/icons/missing_state_64x64.png")
        self.ready_state_logo = QIcon(":/icons/ready_state_64x64.png")
        self.success_state_logo = QIcon(":/icons/success_state_64x64.png")
        self.fail_state_logo =  QIcon(":/icons/fail_state_64x64.png")

        self.run_thread_logo = QMovie(":/icons/run_thread.gif")
        #
        self.setupAppLogoLabel.setPixmap(self.setup_app_Logo)
        self.selectPackagesDirPushButton.setIcon(self.selectDirIcon)
        self.selectPythonExePathPushButton.setIcon(self.selectDirIcon)
        #
        self.extractionCodeLineEdit.setText("****")
        self.showHideCodeLabel.setPixmap(self.hide_code_logo)
        #
        self.packagesInfoTreeWidget.setColumnWidth(0, 600)
        #
        self.setupStateLogoLabel.setPixmap(self.processing_tip_logo)
        #
        self.setupPushButton.setEnabled(False)
        #
        # 载入依赖包信息
        if self.onlineInstallationRadioButton.isChecked():
            self.packages_info, self.packages_err = load_packages_info(self.package_filename, installation_style="online")
        else:
            self.packages_info, self.packages_err = load_packages_info(self.package_filename, installation_style="offline")
        #
        self.update_treewidget(installation_style="offline")
        #
        # # 临时禁用，目前仅支持离线安装，因为在线安装可能无法从http://www.lfd.uci.edu/~gohlke/pythonlibs/下载到合适的版本的依赖包
        # 正常情况，默认应该是“在线安装”
        self.onlineInstallationRadioButton.setEnabled(False)
        # self.offlineInstallationRadioButton.setEnabled(False)
        self.selectPackagesDirLineEdit.setEnabled(True)
        self.selectPackagesDirPushButton.setEnabled(True)

    def connect_signal_slot(self):
        # 可直接设置Label的属性也可实现在浏览器中打开链接的目的：downloadPackagesLFDURLLabel.setOpenExternalLinks(True)
        # 查看是否链接，是否能在点击时，调用浏览器打开
        self.downloadPackagesLFDURLLabel.linkActivated.connect(self.downloadPackagesLFDURLLabellinkActivated)
        self.downloadPackagesPyPIURLLabel.linkActivated.connect(self.downloadPackagesPyPIURLLabellinkActivated)
        self.downloadPackagesBaiduPanURLLabel.linkActivated.connect(self.downloadPackagesBaiduPanURLLabellinkActivated)
        self.showHideCodeLabel.mousePressEvent = self.showHideCodeLabelMousePressEvent
        self.showHideCodeLabel.mouseReleaseEvent = self.showHideCodeLabelMouseReleaseEvent
        #
        self.onlineInstallationRadioButton.clicked.connect(self.onlineInstallationRadioButtonClicked)
        self.offlineInstallationRadioButton.clicked.connect(self.offlineInstallationRadioButtonClicked)
        #
        self.selectPackagesDirPushButton.clicked.connect(self.selectPackagesDirPushButtonClicked)
        self.selectPythonExePathPushButton.clicked.connect(self.selectPythonExePathPushButtonClicked)
        self.selectPackagesDirLineEdit.editingFinished.connect(self.selectPackagesDirLineEditEditingFinished)
        self.setupPushButton.clicked.connect(self.setupPushButtonClicked)
        self.cancelPushButton.clicked.connect(self.cancelPushButtonClicked)

    def downloadPackagesLFDURLLabellinkActivated(self):
        QDesktopServices.openUrl(QUrl("http://www.lfd.uci.edu/~gohlke/pythonlibs/"))

    def downloadPackagesPyPIURLLabellinkActivated(self):
        QDesktopServices.openUrl(QUrl("https://pypi.org/"))

    def downloadPackagesBaiduPanURLLabellinkActivated(self):
        QDesktopServices.openUrl(QUrl("https://pan.baidu.com/s/11FG7T_J7gt56T5nexvRjHw"))

    def showHideCodeLabelMousePressEvent(self, event):
        self.extractionCodeLineEdit.setText("o0zr")
        self.showHideCodeLabel.setPixmap(self.show_code_logo)
    
    def showHideCodeLabelMouseReleaseEvent(self, event):
        self.extractionCodeLineEdit.setText("****")
        self.showHideCodeLabel.setPixmap(self.hide_code_logo)

    def onlineInstallationRadioButtonClicked(self):
        if self.onlineInstallationRadioButton.isChecked():
            self.selectPackagesDirLineEdit.setEnabled(False)
            self.selectPackagesDirPushButton.setEnabled(False)
            #
            self.packages_info, self.packages_err = load_packages_info(self.package_filename, installation_style="online")
            self.packagesInfoTreeWidget.clear()
            self.update_treewidget(installation_style="online")

    def offlineInstallationRadioButtonClicked(self):
        if self.offlineInstallationRadioButton.isChecked():
            self.selectPackagesDirLineEdit.setEnabled(True)
            self.selectPackagesDirPushButton.setEnabled(True)
            #
            self.packages_info, self.packages_err = load_packages_info(self.package_filename, installation_style="offline")
            self.packagesInfoTreeWidget.clear()
            self.update_treewidget(installation_style="offline")

    def selectPythonExePathPushButtonClicked(self):
        file_path, _ =  QFileDialog.getOpenFileName(self, "选择一个测试总体样本文件",
                                                    os.path.expanduser('~'),
                                                    "可执行文件(*.exe)")
        if file_path != "":
            self.selectPythonExePathLineEdit.setText(file_path)
            if self.onlineInstallationRadioButton.isChecked():
                self.setupPushButton.setEnabled(True)

    def selectPackagesDirPushButtonClicked(self):
        file_dir =  QFileDialog.getExistingDirectory(self, "选择一个存储所下载的依赖包的文件目录（文件夹）",
                                                    os.path.expanduser('~'))
        missing_packages_name = []
        if file_dir != "":
            #
            self.selectPackagesDirLineEdit.setText(file_dir)
            #
            for i, packageInfo in enumerate(self.packages_info):
                packageAbsFilePath = file_dir + os.sep + packageInfo[0]
                if os.path.exists(packageAbsFilePath):
                    self.packagesInfoTreeWidget.topLevelItem(i).setText(2, "已就绪")
                    self.packagesInfoTreeWidget.topLevelItem(i).setIcon(2, self.ready_state_logo)
                else:
                    missing_packages_name.append(packageAbsFilePath)
                    self.selectPackagesDirLineEdit.clear()
                    self.setupPushButton.setEnabled(False)
            #
            if len(missing_packages_name) > 0:
                QMessageBox.critical(self, "错误", "找不到以下" + str(len(missing_packages_name)) + \
                                    "个依赖包文件：\n"  + "\n".join(missing_packages_name) + "\n请检查选择依赖包的目录是否正确")
        #
        self.setupPushButton.setEnabled(True)

    def selectPackagesDirLineEditEditingFinished(self):
        file_dir =  self.selectPackagesDirLineEdit.text()
        missing_packages_name = []
        if file_dir != "":
            #
            self.selectPackagesDirLineEdit.setText(file_dir)
            #
            for i, packageInfo in enumerate(self.packages_info):
                packageAbsFilePath = file_dir + os.sep + packageInfo[0]
                if os.path.exists(packageAbsFilePath):
                    self.packagesInfoTreeWidget.topLevelItem(i).setText(2, "已就绪")
                    self.packagesInfoTreeWidget.topLevelItem(i).setIcon(2, self.ready_state_logo)
                else:
                    missing_packages_name.append(packageAbsFilePath)
                    self.selectPackagesDirLineEdit.clear()
                    self.setupPushButton.setEnabled(False)
            #
            if len(missing_packages_name) > 0:
                QMessageBox.critical(self, "错误", "找不到以下" + str(len(missing_packages_name)) + \
                                    "个依赖包文件：\n"  + "\n".join(missing_packages_name) + "\n请检查选择依赖包的目录是否正确")
        #
        self.setupPushButton.setEnabled(True)

    def setupPushButtonClicked(self):
        #
        self.setupPushButton.setEnabled(False)
        self.packages_dir = self.selectPackagesDirLineEdit.text()
        self.python_exe = self.selectPythonExePathLineEdit.text()
        #
        if self.packages_err is not None:
            QMessageBox.critical(self, "错误", "依赖包配置文件中发现无效的配置，请检查纠正！")

        if not os.path.exists(self.python_exe):
            QMessageBox.critical(self, "错误", "Python解释器路径无效，请检查纠正！")

        # 单独处理Windows上，可执行程序的路径中存在类似“Program Files”这样的空格，而导致程序无法执行的情况
        # 方法是：以路径分隔符分割字符串，判断是否子字符串中是否存在空格，存在，则将其替换为双引号引起来的新子字符串
        self.python_exe = check_exe_path(self.python_exe)

        if self.offlineInstallationRadioButton.isChecked() and not os.path.exists(self.packages_dir):
            QMessageBox.critical(self, "错误", "依赖包目录路径无效，请检查纠正！")
        #
        # 注意字符串中的空格，包名称的大小写
        packages_installing_command = [
            [-2, ["PowerShell.exe", self.python_exe + " " + self.get_pip_filename + " " + "--no-warn-script-location"]]
        ]
        if self.onlineInstallationRadioButton.isChecked():
            packages_installing_command.append(
                [-1, ["PowerShell.exe ", self.python_exe + " "  + \
                    "-m pip install pipwin==0.5.1 -i https://pypi.doubanio.com/simple/ --no-warn-script-location"]])
            packages_installing_command.append(
                [0, ["PowerShell.exe", self.python_exe + " "  + "-m pipwin refresh"]]
            )
        #
        # 注意字符串中的空格，包名称的大小写
        for index, package_info in enumerate(self.packages_info):
            package_name = package_info[0]
            package_version = package_info[1]
            if self.onlineInstallationRadioButton.isChecked():
                if package_name == "numpy" or package_name == "gdal" or package_name == "rasterio" or package_name == "fiona":
                    installing_command = ["PowerShell.exe", self.python_exe + " " + \
                                        "-m pipwin install" + " " + package_name + "==" + package_version]
                else:
                    installing_command = ["PowerShell.exe", self.python_exe + " " + \
                                        "-m pip install" + " " + package_name + "==" + package_version + " " + \
                                        "-i https://pypi.doubanio.com/simple/ --no-warn-script-location"]
            else:
                installing_command = ["PowerShell.exe", self.python_exe + " " + \
                                        "-m pip install" + " " + os.path.join(self.packages_dir, package_name) + \
                                        " " + "-i https://pypi.doubanio.com/simple/ --no-warn-script-location"]
            #
            packages_installing_command.append([index + 1, installing_command])

        #   
        self.setupStateLabel.setText("正在安装Python包管理器...")
        self.setupStateLogoLabel.setMovie(self.run_thread_logo)
        self.run_thread_logo.start()
        #
        self.run_install_package_thread = RunInstallPackageThread(packages_installing_command)
        self.run_install_package_thread.update_estimator_data_signal.connect(self.update_installing_info)
        self.stop_thread_signal.connect(self.run_install_package_thread.stop)
        if not self.run_install_package_thread.stop_thread:
            self.run_install_package_thread.start() 

    def update_installing_info(self, return_installing_info):
        #
        command_index = return_installing_info[0]
        status_code = return_installing_info[2]
        error_info = return_installing_info[4]  # error
        #
        if not self.run_install_package_thread.stop_thread:
            # if status_code != 0 or (len(error_info) != 0 and "ERROR" in error_info):
            if status_code != 0:
                if command_index > 0:
                    self.packagesInfoTreeWidget.topLevelItem(command_index - 1).setText(2, "安装失败")
                    self.packagesInfoTreeWidget.topLevelItem(command_index - 1).setIcon(2, self.fail_state_logo)
                #
                self.setupPushButton.setEnabled(True)
                self.stop_thread_signal.emit()
                # 停止子进程
                self.run_install_package_thread.stop()
                # 切换标识子进程运作的动态图标，为成功安装的静态图标
                self.run_thread_logo.stop()
                self.setupStateLogoLabel.setPixmap(self.fail_tip_logo)
                self.setupStateLabel.setText("部分依赖包安装失败，已中断安装程序！")
                QMessageBox.critical(self, "安装错误", "安装依赖包时，发生以下错误：" + error_info)
                return
            else:
                if command_index == -2 or command_index == 0:
                    self.setupStateLabel.setText("正在安装Python依赖包...")
                if command_index >= 1:
                    self.packagesInfoTreeWidget.topLevelItem(command_index - 1).setText(2, "已安装")
                    self.packagesInfoTreeWidget.topLevelItem(command_index - 1).setIcon(2, self.success_state_logo)
            #
            if command_index == len(self.packages_info):
                #
                # 停止子进程
                self.run_install_package_thread.stop()
                # 切换标识子进程运作的动态图标，为成功安装的静态图标
                self.run_thread_logo.stop()
                self.setupStateLabel.setText("依赖包安装完成！")
                self.setupStateLogoLabel.setPixmap(self.finish_tip_logo)
                self.cancelPushButton.setText("关闭")
                #
                # # v0.2.4，更改为通用的Python解释器第三方包安装工具后，不再提供创建iPyGIRS软件启动的Batch脚本的功能
                # bat_filepath = os.path.dirname(os.path.dirname(self.python_exe)) + os.sep + "iPyGIRS.bat"
                # try:
                #     make_run_bat(bat_filepath)
                # except Exception as error_info:
                #     QMessageBox.critical(self, "安装错误", "安装依赖包时，发生以下错误：" + str(error_info))
                #
                QMessageBox.information(self, "安装完成", "所有依赖包已安装成功！")

    def cancelPushButtonClicked(self):
        self.close()


## PyQT5线程：http://www.pythonheidong.com/blog/article/192533/
class RunInstallPackageThread(QThread):

    update_estimator_data_signal = pyqtSignal(list)

    def __init__(self, installing_commands):
        super().__init__()
        self.installing_commands = installing_commands
        self.stop_thread = False

    def run_command(self, index, command):
        process = subprocess.Popen(command, shell = True,
                                encoding = "gb18030",
                                # stdin = subprocess.PIPE,
                                stdout = subprocess.PIPE,
                                stderr = subprocess.PIPE)
        output, err  = process.communicate(timeout = 300)
        # sub_process_instance = sub_process.run()
        status_code = process.returncode
        #
        return [index, command, status_code, output, err]

    def run(self):
        for index, command in self.installing_commands:
            if not self.stop_thread:
                installing_result = self.run_command(index, command)
                self.update_estimator_data_signal.emit(installing_result)
            else:
                break
    
    def stop(self):
        self.stop_thread = True

def main():
    app = QApplication(sys.argv)
    iPyGIRSInstallerDialog_ = iPyGIRSInstallerDialog()
    iPyGIRSInstallerDialog_.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
